// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

//! New request player implementation (based on direct Request deserialization)

use std::collections::VecDeque;
use std::path::Path;

use crate::ipc::bytewise::BytewiseReadOwned;
use crate::types::xgpu_recorder::v1::*;

use super::logger::create_reader;
use super::{format::RecordReader, serializer, RecorderError};

/// Structure containing block and readable object, used to solve lifetime issues
pub struct BlockWithBytewise<T> {
    pub block: RecordBlock,
    _phantom: std::marker::PhantomData<T>,
}

impl<T: BytewiseReadOwned> BlockWithBytewise<T> {
    /// Get readable object reference
    pub fn bytewise(&mut self) -> Result<T, RecorderError> {
        let payload = self
            .block
            .payload
            .as_mut()
            .ok_or_else(|| RecorderError::FormatError {
                message: "Missing payload in record block".to_string(),
            })?;

        // Use new binary deserializer
        serializer::deserialize_record(&mut payload.request_data)
    }
}

/// New request player (simplified design)
pub struct RequestPlayer {
    file_reader: Box<dyn RecordReader>,
    file_header: RecorderHeader,
    current_position: u64,
    blocks_cache: VecDeque<RecordBlock>,
    eof_reached: bool,
}

impl RequestPlayer {
    /// Create player from file
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, RecorderError> {
        let mut file_reader = create_reader(path)?;
        let file_header = file_reader.read_header()?;

        // Validate file header
        Self::validate_header(&file_header)?;

        Ok(Self {
            file_reader,
            file_header,
            current_position: 0,
            blocks_cache: VecDeque::new(),
            eof_reached: false,
        })
    }

    /// Read next record block
    pub fn next_block(&mut self) -> Result<Option<RecordBlock>, RecorderError> {
        // Check cache first
        if let Some(block) = self.blocks_cache.pop_front() {
            self.current_position += 1;
            return Ok(Some(block));
        }

        // Return None if end of file is reached
        if self.eof_reached {
            return Ok(None);
        }

        // Read next block from file
        match self.file_reader.read_block()? {
            Some(block) => {
                self.current_position += 1;
                Ok(Some(block))
            }
            None => {
                self.eof_reached = true;
                Ok(None)
            }
        }
    }

    /// Rebuild complete readable object from record block (using direct deserialization)
    pub fn rebuild_bytewise<T: BytewiseReadOwned>(
        block: &mut RecordBlock,
    ) -> Result<T, RecorderError> {
        let payload = block
            .payload
            .as_mut()
            .ok_or_else(|| RecorderError::FormatError {
                message: "Missing payload in record block".to_string(),
            })?;

        // Use new binary deserializer
        serializer::deserialize_record(&mut payload.request_data)
    }

    /// Read next readable object, returning structure containing block and object
    pub fn next_bytewise<T: BytewiseReadOwned>(
        &mut self,
    ) -> Result<Option<BlockWithBytewise<T>>, RecorderError> {
        match self.next_block()? {
            Some(block) => Ok(Some(BlockWithBytewise {
                block,
                _phantom: std::marker::PhantomData,
            })),
            None => Ok(None),
        }
    }

    /// Reset to file beginning
    pub fn reset(&mut self) -> Result<(), RecorderError> {
        self.file_reader.seek(0)?;

        // Re-read file header
        self.file_header = self.file_reader.read_header()?;

        // Clear cache and status
        self.blocks_cache.clear();
        self.current_position = 0;
        self.eof_reached = false;

        Ok(())
    }

    /// Get file header information
    pub fn header(&self) -> &RecorderHeader {
        &self.file_header
    }

    /// Get current position
    pub fn position(&self) -> u64 {
        self.current_position
    }

    /// Validate file header
    fn validate_header(header: &RecorderHeader) -> Result<(), RecorderError> {
        // Check magic number
        if header.magic != JOURNAL_MAGIC {
            return Err(RecorderError::FormatError {
                message: format!("Invalid magic number: 0x{:08x}", header.magic),
            });
        }

        // Check version compatibility
        if let Some(version) = &header.version {
            if version.major != 1 {
                return Err(RecorderError::FormatError {
                    message: format!("Unsupported major version: {}", version.major),
                });
            }
        }

        Ok(())
    }
}

/// Implement Iterator trait to support iterating record blocks
impl Iterator for RequestPlayer {
    type Item = Result<RecordBlock, RecorderError>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.next_block() {
            Ok(Some(block)) => Some(Ok(block)),
            Ok(None) => None,
            Err(e) => Some(Err(e)),
        }
    }
}

/// Player builder
#[derive(Default)]
pub struct RequestPlayerBuilder {
    validate_checksums: bool,
    cache_size: usize,
}

impl RequestPlayerBuilder {
    pub fn new() -> Self {
        Self {
            validate_checksums: true,
            cache_size: 1000,
        }
    }

    pub fn validate_checksums(mut self, validate: bool) -> Self {
        self.validate_checksums = validate;
        self
    }

    pub fn cache_size(mut self, size: usize) -> Self {
        self.cache_size = size;
        self
    }

    pub fn build<P: AsRef<Path>>(self, path: P) -> Result<RequestPlayer, RecorderError> {
        RequestPlayer::from_file(path)
    }
}
